/* This file is part of libtrapper by Johannes Bauer (JohannesBauer@gmx.de) and released under the GPL-2 */

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#define HEXDUMP_LEADINGSPACE			3
#define HEXDUMP_CHARSPERLINE			16
#define HEXDUMP_HASBORDER				1
#define HEXDUMP_HASADDRHEADER			1
#define HEXDUMP_HASHEXADDRESS			1
#define HEXDUMP_HASTEXTDUMP				1
#define HEXDUMP_UNICODEBORDER

#ifdef HEXDUMP_UNICODEBORDER
#define HEXDUMP_LN_H					"─"
#define HEXDUMP_LN_V					"│"
#define HEXDUMP_LN_DR					"┌"
#define HEXDUMP_LN_LDR					"┬"
#define HEXDUMP_LN_DL					"┐"
#define HEXDUMP_LN_LRDU					"┼"
#define HEXDUMP_LN_UR					"└"
#define HEXDUMP_LN_LUR					"┴"
#define HEXDUMP_LN_UL					"┘"
#define HEXDUMP_LN_DUR					"├"
#define HEXDUMP_LN_DUL					"┤"
#else
#define HEXDUMP_LN_H					"-"
#define HEXDUMP_LN_V					"|"
#define HEXDUMP_LN_DR					"+"
#define HEXDUMP_LN_LDR					"+"
#define HEXDUMP_LN_DL					"+"
#define HEXDUMP_LN_LRDU					"+"
#define HEXDUMP_LN_UR					"+"
#define HEXDUMP_LN_LUR					"+"
#define HEXDUMP_LN_UL					"+"
#define HEXDUMP_LN_DUR					"+"
#define HEXDUMP_LN_DUL					"+"
#endif
#if (HEXDUMP_CHARSPERLINE % 2) != 0
#error "Number of characters per line must be divisible by two."
#endif
#if HEXDUMP_CHARSPERLINE < 2
#error "Must have at least two characters per line."
#endif
#define SEPERATOR_AFTER			((HEXDUMP_CHARSPERLINE) / 2)

void FileHexDump(FILE *Destination, const void *RawData, unsigned int Size) {
	unsigned int i, j;
	int HaveShorterLine = 0;
	unsigned int NumberLines = Size / HEXDUMP_CHARSPERLINE;

	if ((Size % HEXDUMP_CHARSPERLINE) != 0) {
		NumberLines++;
		HaveShorterLine = 1;
	}

	if (HEXDUMP_HASADDRHEADER) {
		if (HEXDUMP_HASBORDER) {
			for (j = 0; j < HEXDUMP_LEADINGSPACE; j++) fprintf(Destination, " ");
			if (HEXDUMP_HASHEXADDRESS) for (i = 0; i < 6; i++) fprintf(Destination, " ");
			fprintf(Destination, HEXDUMP_LN_DR);
			for (j = 0; j < (HEXDUMP_CHARSPERLINE * 3) + 5; j++) fprintf(Destination, HEXDUMP_LN_H);
			fprintf(Destination, HEXDUMP_LN_DL);
			fprintf(Destination, "\n");
		}

		for (j = 0; j < HEXDUMP_LEADINGSPACE; j++) fprintf(Destination, " ");
		if (HEXDUMP_HASHEXADDRESS) for (i = 0; i < 6; i++) fprintf(Destination, " ");
		if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);
		fprintf(Destination, " ");
		for (j = 0; j < HEXDUMP_CHARSPERLINE; j++) {
			fprintf(Destination, "%-3x", j);
			if (j == SEPERATOR_AFTER - 1) fprintf(Destination, "    ");
		}
		if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);
		fprintf(Destination, "\n");
		
		for (j = 0; j < HEXDUMP_LEADINGSPACE; j++) fprintf(Destination, " ");
		if (HEXDUMP_HASHEXADDRESS) for (i = 0; i < 6; i++) fprintf(Destination, " ");
		if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);
		fprintf(Destination, " ");
		for (j = 0; j < HEXDUMP_CHARSPERLINE; j++) {
			fprintf(Destination, "%-3d", j);
			if (j == SEPERATOR_AFTER - 1) fprintf(Destination, "    ");
		}
		if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);
		fprintf(Destination, "\n");
	}

	if (HEXDUMP_HASBORDER) {
		for (j = 0; j < HEXDUMP_LEADINGSPACE; j++) fprintf(Destination, " ");
		if ((HEXDUMP_HASHEXADDRESS) || (!HEXDUMP_HASADDRHEADER)) fprintf(Destination, HEXDUMP_LN_DR);
			else fprintf(Destination, HEXDUMP_LN_DUR);
		if (HEXDUMP_HASHEXADDRESS) {
			for (i = 0; i < 5; i++) fprintf(Destination, HEXDUMP_LN_H);
			if (!HEXDUMP_HASADDRHEADER) fprintf(Destination, HEXDUMP_LN_LDR);
				else fprintf(Destination, HEXDUMP_LN_LRDU);
		}

		for (j = 0; j < (HEXDUMP_CHARSPERLINE * 3) + 5; j++) fprintf(Destination, HEXDUMP_LN_H);

		if (HEXDUMP_HASTEXTDUMP) {
			if (!HEXDUMP_HASADDRHEADER) fprintf(Destination, HEXDUMP_LN_LDR);
				else fprintf(Destination, HEXDUMP_LN_LRDU);
			for (j = 0; j < HEXDUMP_CHARSPERLINE; j++) fprintf(Destination, HEXDUMP_LN_H);
		}
		
		if ((HEXDUMP_HASTEXTDUMP) || (!HEXDUMP_HASADDRHEADER)) fprintf(Destination, HEXDUMP_LN_DL);
			else fprintf(Destination, HEXDUMP_LN_DUL);

		fprintf(Destination, "\n");
	}


	for (i = 0; i < NumberLines; i++) {
		unsigned int NumberCharacters = HEXDUMP_CHARSPERLINE;
		if ((i == NumberLines - 1) && HaveShorterLine) {
			NumberCharacters = Size % HEXDUMP_CHARSPERLINE;
		}

		for (j = 0; j < HEXDUMP_LEADINGSPACE; j++) fprintf(Destination, " ");

		if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);

		if (HEXDUMP_HASHEXADDRESS) {
			fprintf(Destination, "%4x ", i * HEXDUMP_CHARSPERLINE);
			if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);
		}
		fprintf(Destination, " ");

		for (j = 0; j < NumberCharacters; j++) {
			fprintf(Destination, "%02x ", ((unsigned char*)RawData)[(i * HEXDUMP_CHARSPERLINE) + j]);
			if ((j == (SEPERATOR_AFTER - 1)) && (j != NumberCharacters - 1)) fprintf(Destination, " -  ");
		}
		
		if (NumberCharacters < HEXDUMP_CHARSPERLINE) {
			/* Fill line up */
			for (j = 0; j < HEXDUMP_CHARSPERLINE - NumberCharacters; j++) {
				fprintf(Destination, "   ");
			}
			if (NumberCharacters <= SEPERATOR_AFTER) fprintf(Destination, "    ");
		}
		
		if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);

		if (HEXDUMP_HASTEXTDUMP) {
			for (j = 0; j < NumberCharacters; j++) {
				char Character = ((char*)RawData)[(i * HEXDUMP_CHARSPERLINE) + j];
				if ((Character <= 32) || (Character >= 127)) Character = '.';
				fprintf(Destination, "%c", Character);
			}
			for (j = 0; j < HEXDUMP_CHARSPERLINE - NumberCharacters; j++) fprintf(Destination, " ");
			if (HEXDUMP_HASBORDER) fprintf(Destination, HEXDUMP_LN_V);
		}

		fprintf(Destination, "\n");
	}
		
	if (HEXDUMP_HASBORDER) {
		for (j = 0; j < HEXDUMP_LEADINGSPACE; j++) fprintf(Destination, " ");
		fprintf(Destination, HEXDUMP_LN_UR);
		if (HEXDUMP_HASHEXADDRESS) {
			for (i = 0; i < 5; i++) fprintf(Destination, HEXDUMP_LN_H);
			fprintf(Destination, HEXDUMP_LN_LUR);
		}

		for (j = 0; j < (HEXDUMP_CHARSPERLINE * 3) + 5; j++) fprintf(Destination, HEXDUMP_LN_H);

		if (HEXDUMP_HASTEXTDUMP) {
			fprintf(Destination, HEXDUMP_LN_LUR);
			for (j = 0; j < HEXDUMP_CHARSPERLINE; j++) fprintf(Destination, HEXDUMP_LN_H);
		}
		
		fprintf(Destination, HEXDUMP_LN_UL);

		fprintf(Destination, "\n");
	}
}

void HexDump(const void *RawData, unsigned int Size) {
	FileHexDump(stderr, RawData, Size);
}

